
#include  <stdio.h>      
#include  <stdlib.h>    
#include  <unistd.h>    
#include  <sys/types.h>  
#include  <sys/stat.h>   
#include  <fcntl.h>     
#include  <termios.h>    
#include  <errno.h>   
#include  <string.h>  
#include  <assert.h>
#include "uart.h"

static int fd;

/*******************************************************************
* 名称：                  UART0_Open
* 功能：                打开串口并返回串口设备文件描述
* 入口参数：        fd    :文件描述符     port :串口号(ttyS0,ttyS1,ttyS2)
* 出口参数：        正确返回为1，错误返回为0
*******************************************************************/  
int uart_open(int fd,const char *pathname)
{
	assert(pathname);

	fd = open(pathname,O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);
	if(fd == -1)
	{
		perror("Open UART failed!");
		return FALSE;
	}
	/*恢复串口为阻塞状态,阻塞状态即在读写串口的过程中，
	*	如果没有读取到足够的数据，则会一直等待，直到出现对应的数据为止，可以使用select绕过阻塞
	*非阻塞：fcntl(fd,F_SETFL,FNDELAY)
	*/
		        
	if(fcntl(fd, F_SETFL, 0) < 0)  
	{  
		printf("fcntl failed!\n");  
		return FALSE;  
	}       
	else  
	{  
	//	printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));  
		fcntl(fd, F_SETFL,0);
	}  
	//测试是否为终端设备      
	if(0 == isatty(STDIN_FILENO))  
	{  
		printf("standard input is not a terminal device\n");  
		return FALSE;  
	}  
	else  
	{  
//		printf("isatty success!\n");  
	}                
//	printf("fd->open=%d\n",fd);  
	return fd;  
}
/*******************************************************************
* 名称：                uart_close
* 功能：                关闭串口并返回串口设备文件描述
* 入口参数：        fd    :文件描述符     port :串口号(ttyS0,ttyS1,ttyS2)
* 出口参数：        void
*******************************************************************/  
void uart_close(int fd)  
{  
    close(fd);  
}  
/*******************************************************************
* 名称：                 uart_set
* 功能：                设置串口数据位，停止位和效验位
* 入口参数：        fd        串口文件描述符
*                              speed     串口速度
*                              flow_ctrl   数据流控制
*                           databits   数据位   取值为 7 或者8
*                           stopbits   停止位   取值为 1 或者2
*                           parity     效验类型 取值为N,E,O,,S
*出口参数：          正确返回为1，错误返回为0
*******************************************************************/  
int uart_set(int fd,int baude,int c_flow,int bits,char parity,int stop)
{
	
	struct termios options;
	/*tcgetattr(fd,&options)得到与fd指向对象的相关参数，并将它们保存于options,
	*该函数还可以测试配置是否正确，该串口是否可用等。若调用成功，函数返回值为0，若调用失败，函数返回值为1.
	*/  
	if(tcgetattr(fd,&options) < 0)
	{
		perror("tcgetattr error");
		return -1;
	}
     //设置串口输入波特率和输出波特率  
     switch(baude)
	{
		case 115200:
			cfsetispeed(&options, B115200);   
			cfsetospeed(&options, B115200);
//			printf("enter bande 115200\n");
			break;
		case 9600:
			cfsetispeed(&options, B9600);   
 			cfsetospeed(&options, B9600);
//			printf("enter bande 9600\n");
			break;
		default:
//			printf("enter bande Error \n");
			break;
	}

     /*设置控制模式*/
    options.c_cflag |= CLOCAL;//保证程序不占用串口
    options.c_cflag |= CREAD;//保证程序可以从串口中读取数据

     /*设置数据流控制*/
     switch(c_flow)
     {
          case 0://不进行流控制
               options.c_cflag &= ~CRTSCTS;
//			   printf("no c_flow \n");
               break;
          case 1://进行硬件流控制
               options.c_cflag |= CRTSCTS;
               break;
          case 2://进行软件流控制
               options.c_cflag |= IXON|IXOFF|IXANY;
               break;
          default:
//             fprintf(stderr,"Unkown c_flow!\n");
               return -1;
     }

    /*设置数据位*/
    switch(bits)
    {
        case 5:
            options.c_cflag &= ~CSIZE;//屏蔽其它标志位
            options.c_cflag |= CS5;
            break;
        case 6:
            options.c_cflag &= ~CSIZE;//屏蔽其它标志位
            options.c_cflag |= CS6;
            break;
        case 7:
            options.c_cflag &= ~CSIZE;//屏蔽其它标志位
            options.c_cflag |= CS7;
            break;
        case 8:
            options.c_cflag &= ~CSIZE;//屏蔽其它标志位
            options.c_cflag |= CS8;
//			printf("CS8 \n");
            break;
        default:
//            fprintf(stderr,"Unkown bits!\n");
            return -1;
    }

    /*设置校验位*/
    switch(parity)
    {
        /*无奇偶校验位*/
        case 'n':
        case 'N':
            options.c_cflag &= ~PARENB;//PARENB：产生奇偶位，执行奇偶校验
            options.c_iflag &= ~INPCK;//INPCK：使奇偶校验起作用 /*此处一定要注意，网上都是错误的，全是坑货，再就是要阅读相关的手册*/
//			fprintf(stderr,"parity n \n");
            break;
        /*设为空格,即停止位为2位*/
        case 's':
        case 'S':
            options.c_cflag &= ~PARENB;//PARENB：产生奇偶位，执行奇偶校验
            options.c_cflag &= ~CSTOPB;//CSTOPB：使用两位停止位
            break;
        /*设置奇校验*/
        case 'o':
        case 'O':
            options.c_cflag |= PARENB;//PARENB：产生奇偶位，执行奇偶校验
            options.c_cflag |= PARODD;//PARODD：若设置则为奇校验,否则为偶校验
            options.c_iflag |= INPCK;//INPCK：使奇偶校验起作用
            options.c_cflag |= ISTRIP;//ISTRIP：若设置则有效输入数字被剥离7个字节，否则保留全部8位
            break;
        /*设置偶校验*/
        case 'e':
        case 'E':
            options.c_cflag |= PARENB;//PARENB：产生奇偶位，执行奇偶校验
            options.c_cflag &= ~PARODD;//PARODD：若设置则为奇校验,否则为偶校验
            options.c_iflag |= INPCK;//INPCK：使奇偶校验起作用
            options.c_cflag |= ISTRIP;//ISTRIP：若设置则有效输入数字被剥离7个字节，否则保留全部8位
            break;
        default:
//            fprintf(stderr,"Unkown parity!\n");
            return -1;
    }

    /*设置停止位*/
    switch(stop)
    {
        case 1:
            options.c_cflag &= ~CSTOPB;//CSTOPB：使用两位停止位
//			fprintf(stderr,"stop 1 \n");
            break;
        case 2:
            options.c_cflag |= CSTOPB;//CSTOPB：使用两位停止位
            break;
        default:
//            fprintf(stderr,"Unkown stop!\n");
            return -1;
    }

	/*设置输出模式为原始输出*/
	options.c_oflag &= ~OPOST;//OPOST：若设置则按定义的输出处理，否则所有c_oflag失效

	options.c_oflag &= ~(ONLCR|OCRNL);
	/*设置本地模式为原始模式*/
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
//	options.c_lflag &= ~(ISIG | ICANON);  

	options.c_iflag &=~(ICRNL|INLCR|BRKINT|IXON|IXOFF|IXANY);
	
	/*
	*ICANON：允许规范模式进行输入处理
	*ECHO：允许输入字符的本地回显
	*ECHOE：在接收EPASE时执行Backspace,Space,Backspace组合
	*ISIG：允许信号
	*/

	/*使用二进制，删除不必要的控制符号*/
//	options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | IGNCR);

	/*设置等待时间和最小接受字符*/
	options.c_cc[VTIME] = 0;//可以在select中设置
	options.c_cc[VMIN] = 1;//最少读取一个字符

	/*如果发生数据溢出，只接受数据，但是不进行读操作*/
	tcflush(fd,TCIFLUSH);

     /*激活配置*/
     if(tcsetattr(fd,TCSANOW,&options) < 0)
     {
          perror("tcsetattr failed");
          return -1;
     }

     return TRUE;

}

/*******************************************************************
* 名称：                uart_init()
* 功能：                串口初始化
* 入口参数：        fd       :  文件描述符    
*               speed  :  串口速度
*                              flow_ctrl  数据流控制
*               databits   数据位   取值为 7 或者8
*                           stopbits   停止位   取值为 1 或者2
*                           parity     效验类型 取值为N,E,O,,S
*                       
* 出口参数：        正确返回为1，错误返回为0
*******************************************************************/  
int uart_init(int fd, int speed,int flow_ctrl,int databits,int stopbits,int parity)  
{  
     int err;  
     if (uart_set(fd,speed,flow_ctrl,databits,'N',1) == FALSE)  
     {                                                           
          return FALSE;  
     }  
     else  
     {  
          return  TRUE;  
     }  
}  
/*******************************************************************
* 名称：                  uart_recv
* 功能：                接收串口数据
* 入口参数：        fd                  :文件描述符     
*                              rcv_buf     :接收串口中数据存入rcv_buf缓冲区中
*                              data_len    :一帧数据的长度
* 出口参数：        正确返回为1，错误返回为0
*******************************************************************/  
int uart_recv(int fd, char *rcv_buf,int data_len)  
{  
    int len,fs_sel;  
    fd_set fs_read;  
     /*fd_set是一组文件描述字(fd)的集合，
      * 它用一位来表示一个fd,定义在头文件<sys/select.h>中定义常量FD_SETSIZE,一般可表示1024个ID
     */
     
    struct timeval time;  
     
    FD_ZERO(&fs_read);  /*初始化读取的集合*/
    FD_SET(fd,&fs_read);  /*用于在文件描述符集合中增加一个新的文件描述符*/
     
    time.tv_sec = 0; /*等待1秒，超时则会退出*/
    time.tv_usec = 10000;  
     
    //使用select实现串口的多路通信  
    fs_sel = select(fd+1,&fs_read,NULL,NULL,&time);  
//   printf("fs_sel = %d\n",fs_sel);  
    if(fs_sel)  
    {  
        len = read(fd,rcv_buf,data_len);  
//        printf("I am right!(version1.2) len = %d fs_sel = %d\n",len,fs_sel);  
        return len;  
    }  
    else  
    {  
 //       printf("[uart.c] do not get any data! \n");  
        return FALSE;  
    }       
}  
/********************************************************************
* 名称：                  uart_send
* 功能：                发送数据
* 入口参数：        fd                  :文件描述符     
*                              send_buf    :存放串口发送数据
*                              data_len    :一帧数据的个数
* 出口参数：        正确返回为1，错误返回为0
*******************************************************************/  
int uart_send(int fd, char *send_buf,int data_len)  
{  
    int len = 0;  
	len = write(fd,send_buf,data_len);  
    if (len == data_len )  
    {  
//        printf("send data len is %d",len);
        return len;  
    }       
    else     
    {                   
        tcflush(fd,TCOFLUSH);  
        return FALSE;  
    }   
}  

   

